/**
 * Created by user on 2016-07-20.
 */
import * as config from 'config'
import * as co from 'co'
import * as jwt from 'jsonwebtoken'
import {superApi, normalApi, accountApi, rechargeAfter} from './api';
import GMModel from '../database/models/gm';
import GMCommandHistory from '../database/models/gm-command-history';
import Player from '../database/models/player';
import GM from '../database/models/gm';
import RoomRecord from '../database/models/roomRecord'
import ExtRecord from '../database/models/extRecord';
import {sign, verify} from './authSign'
import GameRecord from "../database/models/gameRecord"
import serverApi from './deps/server-api'
import storeFactory from './store'
import IAPVerifier from "./IAP";
import IOSRecord from "../database/models/isoRecord";

const JwtStrategy = require('passport-jwt').Strategy
const ExtractJwt = require('passport-jwt').ExtractJwt
const jwtSecret = config.get('gm-tool.jwt.secret')

const store = storeFactory.getInstance()

const productIdToRes = {
  "com.fanmeng.pucheng.shisanshui_fangka_0002": {gem: 2, gold: 0},
  "com.fanmeng.pucheng.shisanshui_fangka_0006": {gem: 6, gold: 0},
  "com.fanmeng.pucheng.shisanshui_fangka_0015": {gem: 15, gold: 40},
  "com.fanmeng.pucheng.shisanshui_fangka_0025": {gem: 25, gold: 0},
  "com.fanmeng.pucheng.shisanshui_fangka_0105": {gem: 105, gold: 0},
  "com.fanmeng.pucheng.shisanshui_fangka_0250": {gem: 250, gold: 0}
}

export default function (app, passport) {
  const opts = {}
  opts.jwtFromRequest = ExtractJwt.fromAuthHeader()
  opts.secretOrKey = jwtSecret
  opts.passReqToCallback = true

  passport.use(new JwtStrategy(opts, verifyJWT))
  passport.serializeUser((u, done) => done(null, u))
  passport.deserializeUser((u, done) => done(null, u))

  app.get('/', (req, res) => res.status(200).end('Hello'))
  app.get('/public/status', async (req, res) => {
    const status = await superApi['server/status']()
    res.json(status)
  })
  app.post('/account/login', login)
  app.post('/account/logout', passport.authenticate('jwt'), logout)
  app.post('/account/info', passport.authenticate('jwt'), isNormalGM, accountApi.info);
  app.post('/account/records', passport.authenticate('jwt'), isNormalGM, accountApi.records);
  app.post('/account/gm-records', passport.authenticate('jwt'), isNormalGM, accountApi.gmRecords);
  app.post('/super/players/count', passport.authenticate('jwt'), isSuperGM, getPlayerCount)
  app.post('/super/players', passport.authenticate('jwt'), isSuperGM, getPlayers)
  app.post('/super/gameRecord', passport.authenticate('jwt'), isSuperGM, getGameRecordPage)
  app.post('/super/room/:num', passport.authenticate('jwt'), isSuperGM, getRoom)
  app.post('/super/replay/:id', passport.authenticate('jwt'), isSuperGM, getReply)
  app.post('/ext/recharge', isAuthedExt, extRecharge)

  const url = process.env.NODE_ENV === "production" ? 'https://buy.itunes.apple.com/verifyReceipt' : 'https://sandbox.itunes.apple.com/verifyReceipt'
  app.post('/ext/ios/iap', createIAPMiddleWare(url, productIdToRes))

  registerWrapperApi(app, passport)
}


function createIAPMiddleWare(url, productId2ResMap) {
  const verifier = new IAPVerifier(url)

  return async function (req, res) {

    const receiptRes = await verifier.validate({'receipt-data': req.body['receipt-data']})
    const shortId = req.body.shortId

    if (receiptRes.status === 0) {
      try {

        console.log(`${__filename}:79 `, receiptRes.receipt.product_id);

        const resource = productId2ResMap[receiptRes.receipt.product_id]

        if (!resource) {
          throw Error('WRONG_PRODUCT_ID')
        }

        const usedTran = await IOSRecord.findOne({transactionId: receiptRes.receipt.transaction_id})
        if (usedTran) {
          throw Error('EXISTED_TRANSACTION')
        }
        const {gem, gold} = resource

        await new IOSRecord({
          shortId,
          gem,
          gold,
          transactionId: receiptRes.receipt.transaction_id,
          productId: receiptRes.receipt.product_id,
          receipt: req.body['receipt-data']
        }).save()


        const model = await Player.findOne({shortId}).populate('inviteBy').exec();

        if (!model) {
          return res.status(400).json({ok: false, msg: '用户不存在'})
        }

        const _id = model._id
        await serverApi.addResource(_id, gem, gold)
        await Player.update({_id}, {
          $inc: {gem: gem, gold: gold},
        }).exec();


        await new ExtRecord({
          uid: model._id,
          shortId,
          gem,
          gold,
          oid: `app:${receiptRes.receipt.transaction_id}`,
          from: 'ios'
        }).save()
        await saveExtRechargeRecord(model, gem, gold);

        res.json({ok: true})
      } catch (e) {
        console.log(`${__filename}:145 extRecharge`, e)
        res.status(500)
          .json({ok: false, msg: '服务器错误' + e.message})
      }
    }

  }
}


function genAuthString(payload) {
  const encrypted = jwt.sign(payload, jwtSecret)
  return `JWT ${encrypted}`
}

async function isTokenValid({token, userId, rand}) {

  if (verify(`${userId + rand}=${token}`)) {
    const saveId = await store.getAsync(token)

    if (!saveId) {
      return false
    }

    return userId === saveId.userId
  }

  return false
}

async function verifyJWT(req, jwt_payload, done) {
  if (jwt_payload.token && await isTokenValid(jwt_payload)) {
    const {userId, token} = jwt_payload
    const user = await GMModel.findOne({_id: userId}).lean().exec()
    req.user = user
    req.token = token.toString()
    return done(null, user)

  }

  done(null, null)
}

async function login(req, res) {
  const {username, password} = req.body
  const gm = await GMModel.findOne({username})
  if (!gm) {
    return res.json({success: false})
  }

  if (!gm.validPassword(password)) {
    return res.json({success: false})
  }

  const userId = gm._id.toString()
  const rand = Math.random()
  const token = sign(userId + rand)
  await store.setAsync(token, {userId})
  const accessToken = genAuthString({token, userId, rand})
  return res.json({success: true, user: gm, accessToken})
}


async function logout(req, res) {
  await store.destroyAsync(req.token)
  res.json({success: true})
}


function isSuperGM(req, res, next) {
  if (req.isAuthenticated()) {
    if (req.user.role === 'super') {
      return next();
    }
  }
  return res.json({success: false, message: '你不是超级GM'});
}

function isNormalGM(req, res, next) {
  if (req.isAuthenticated()) {
    return next();
  }
  return res.json({success: false, message: '你不是GM'});
}

function isAuthedExt(req, res, next) {
  const {shortId, gem, gold, oid, key} = req.body

  const toVerify = `${[shortId, gem, oid, gold].join(',')}=${key}`

  if (verify(toVerify)) {
    return next()
  } else {
    return res.status(400).json({ok: false, msg: '签名错误'})
  }

}


async function extRecharge(req, res) {
  try {
    const {shortId, gem, gold, oid, key} = req.body

    const model = await Player.findOne({shortId}).populate('inviteBy').exec();

    if (!model) {
      return res.status(400).json({ok: false, msg: '用户不存在'})
    }

    const _id = model._id
    await serverApi.addResource(_id, gem, gold)
    await Player.update({_id}, {
      $inc: {gem: gem, gold: gold},
    }).exec();


    await new ExtRecord({uid: model._id, shortId, gem, gold, oid, from: 'wechat'}).save()
    await saveExtRechargeRecord(model, gem, gold);

    res.json({ok: true, oid})
  } catch (e) {
    console.log(`${__filename}:145 extRecharge`, e)
    res.status(500)
      .json({ok: false, msg: '服务器错误' + e.message})
  }
}


const kickbackPercent = config.get('gm-tool.recharge.kickback')
const kickback2Percent = config.get('gm-tool.recharge.kickback2')

async function saveExtRechargeRecord(model, gem, gold) {
  const source = 'ext'
  const amount = gem || gold
  const currency = gem ? 'gem' : 'gold'
  const player = model
  const gm = await GM.findOne({role: 'super'}).lean().exec()
  let relation = []
  let kickback = 0
  let kickback2 = 0

  if (player.inviteBy) {
    relation = [player.inviteBy._id, player.inviteBy.superior]
    kickback = Math.floor(amount * kickbackPercent)
    kickback2 = Math.floor(amount * kickback2Percent)
  }

  await rechargeAfter({
    gm,
    player,
    relation,
    currency,
    source,
    amount,
    kickback,
    kickback2
  })
}


function getReply(req, res) {
  return co(function* () {
    const replayId = req.params.id

    const replay = yield GameRecord.findOne({_id: replayId}).sort({time: -1}).exec()
    res.json({success: true, replay})

  }).catch((err) => {
    res.json({success: false, message: err.message})
  })
}

function getRoom(req, res) {
  return co(function* () {
    const roomNum = req.params.num
    const records = yield RoomRecord.find({roomNum})
      .sort({createAt: -1})
      .limit(5)
      .lean()
      .exec()

    for (let r of records) {
      const room = r.room
      r.replayList = yield GameRecord.find({room}).select({record: 1, time: 1})
        .sort({time: -1})
        .exec()
    }


    res.json({success: true, gameRecords: records})
  }).catch((err) => {
    res.json({success: false, message: err.message})
  })
}


function getGameRecordPage(req, res) {
  return co(function* () {
    const page = Number(req.body.page) || 0
    const records = yield RoomRecord.find()
      .sort({createAt: -1})
      .skip(page * 10)
      .limit(10)
      .lean()
      .exec()
    const recordCount = yield RoomRecord.count()
    const pageCount = Math.ceil(recordCount / 10)
    res.json({success: true, gameRecords: records, pageCount})
  }).catch((err) => {
    res.json({success: false, message: err.message})
  })
}

function getPlayerCount(req, res) {
  return co(function* co() {
    const [allPlayers, weixinPlayers] = yield [Player.count(), Player.count({isTourist: false})]
    res.json({success: true, allPlayers, weixinPlayers})
  }).catch((err) => {
    res.json({success: false, message: err.message})
  })
}

function getPlayers(req, res) {
  return co(function* co() {

    const page = Number(req.body.page) || 0
    const players = yield Player.find({isTourist: false})
      .populate('inviteBy')
      .sort({createAt: -1})
      .skip(page * 10)
      .limit(10)
      .lean()
      .exec()

    const playerTotal = yield Player.count({isTourist: false})
    const pageCount = Math.ceil(playerTotal / 10)
    res.json({success: true, players, pageCount})
  }).catch((err) => {
    res.json({success: false, message: err.message})
  })
}

function registerWrapperApi(app, passport) {
  const apiWrapper = (f) => (
    async (req, res, next) => {
      res.json(await f(req.body, req.user));
      next()
    });

  for (const key of Object.keys(normalApi)) {
    const handler = apiWrapper(normalApi[key]);
    const record = (req) => recordHistory(req, 'normal', key);
    app.post(`/normal/${key}`, passport.authenticate('jwt'), isNormalGM, handler, record);
  }

  const recordHistory = (req, role, command) => {
    if (req.user) {
      const history = new GMCommandHistory({
        username: req.user.username,
        role,
        command,
        data: req.body,
      });
      history.save();
    }
  };

  for (const key of Object.keys(superApi)) {
    const handler = apiWrapper(superApi[key]);
    const record = (req) => recordHistory(req, 'super', key);
    app.post(`/super/${key}`, passport.authenticate('jwt'), isSuperGM, handler, record);
  }
}


